package com.tang.basics.introductory;

import java.util.Scanner;
/*
 * 数组使用注意事项和细节
 * 1) 数组是多个相同类型数据的组合，实现对这些数据的统一管理
 * 2) 数组中的元素可以是任何数据类型，包括基本类型和引用类型，但是不能混用。
 * 3)数组创建后，如果没有赋值，有默认值
 * int 0，short 0, byte 0, long 0, float 0.0,double 0.0，char \u0000，boolean false，String null
 * 4) 使用数组的步骤 1. 声明数组并开辟空间 2 给数组各个元素赋值 3 使用数组
 * 5) 数组的下标是从 0 开始的。 6) 数组下标必须在指定范围内使用，否则报：下标越界异常，比如
 * int [] arr=new int[5]; 则有效下标为 0-4
 *
 * 数组赋值机制
 * 1) 基本数据类型赋值，这个值就是具体的数据，而且相互不影响。 int n1 = 2; int n2 = n1;
 * 2) 数组在默认情况下是引用传递，赋的值是地址。
 * int[] arr1 = {1,2,3};
 * int[] arr2 = arr1;
 * arr2[0]=10
 * System.out.println(arr1[0])//10
 * arr1>>0x0011  保存是地址
 * 0x0011 >> {1，2，3}　数据保存在堆中，通过地址找寻
 * arr2 >> arr1 >>> 0x0011  也就是说把地址拷贝了一份给arr2
 * 修改arr2[0] 也就是修改　arr1[0]
 * */


public class Array_11 {
    public static void main(String[] args) {
//        ArrayExercise01.main();
//         int a=SeqSearch.recursionBinarySearch();
//         System.out.println(a);
        YangHui.main();
    }
}


/*
 * 数组创建
 * 使用方式 -动态初始化
 * 使用方式 -静态初始化
 * */
class CreateArray {
    //使用方式 -动态初始化
    public static void main() {
        //演示 数据类型 数组名[]=new 数据类型[大小]
        //循环输入 5 个成绩，保存到 double 数组,并输出
        //步骤 //1. 创建一个 double 数组，大小 5
        // (1) 第一种动态分配方式
        // double scores[] = new double[5];
        //(2) 第 2 种动态分配方式， 先声明数组，再 new 分配空间
        double scores[]; //声明数组， 这时 scores 是 null
        scores = new double[5]; // 分配内存空间，可以存放数据
        //2. 循环输入
        // scores.length 表示数组的大小/长度
        scores = new double[5]; // 分配内存空间，可以存放数据
        Scanner myScanner = new Scanner(System.in);
        for (int i = 0; i < scores.length; i++) {
            System.out.println("请输入第" + (i + 1) + "个元素的值");
            scores[i] = myScanner.nextDouble();
        }
        //输出，遍历数组
        System.out.println("==数组的元素/值的情况如下:===");
        for (int i = 0; i < scores.length; i++) {
            System.out.println("第" + (i + 1) + "个元素的值=" + scores[i]);
        }
    }

    //使用方式 -静态初始化
    public static void main2() {
        double[] hens = {3, 5, 1, 3.4, 2, 50, 7.8, 88.8, 1.1, 5.6, 100};
    }
}


class Array01 {
    public static void main() {
        double[] hens = {3, 5, 1, 3.4, 2, 50, 7.8, 88.8, 1.1, 5.6, 100};
        //hens[下标] 来访问数组的元素
        //可以通过 数组名.length 得到数组的大小/长度
        double totalWeight = 0;
        for (int i = 0; i < hens.length; i++) {
            //System.out.println("第" + (i+1) + "个元素的值=" + hens[i]);
            totalWeight += hens[i];
        }
        System.out.println("总体重=" + totalWeight + "平均体重=" + (totalWeight / hens.length));


    }
}

//创建一个 char 类型的 26 个元素的数组，分别 放置'A'-'Z'。
//使用 for 循环访问所有元素并打印出来。
//提示：char 类型数据运算 'A'+1 -> 'B
class ArrayExercise01 {
    //思路分析
    //1. 定义一个 数组 char[] chars = new char[26]
    //2. 因为 'A' + 1 = 'B' 类推，所以使用 for 来赋值
    //3. 使用 for 循环访问所有元素
    public static void main() {
        char[] chars = new char[26];
        for (int i = 0; i < chars.length; i++) {
            chars[i] = (char) ('A' + i); //'A' + i 是 int , 需要强制转换
        }

        System.out.println("===chars 数组===");

        for (int i = 0; i < chars.length; i++) {
            System.out.println(chars[i] + "");
        }
    }

}

//请求出一个数组 int[]的最大值 {4,-1,9, 10,23}，并得到对应的下标。
class ArrayExercise02 {
    public static void main() {
        //思路分析
        //1. 定义一个 int 数组 int[] arr = {4,-1,9, 10,23};
        //2. 假定 max = arr[0] 是最大值 , maxIndex=0;
        //3. 从下标 1 开始遍历 arr， 如果 max < 当前元素，说明 max 不是真正的
        // 最大值, 我们就 max=当前元素; maxIndex=当前元素下标
        //4. 当我们遍历这个数组 arr 后 , max 就是真正的最大值，maxIndex 最大值
        //对应的下标
        int[] arr = {4, -1, 9, 10, 23};
        int max = arr[0];//假定第一个元素就是最大值
        int maxIndex = 0; //
        for (int i = 1; i < arr.length; i++) {//从下标 1 开始遍历 arr
            if (max < arr[i]) {//如果 max < 当前元素
                max = arr[i]; //把 max 设置成 当前元素
                maxIndex = i;
            }
        }
        //当我们遍历这个数组 arr 后 , max 就是真正的最大值，maxIndex 最大值下标
        System.out.println("max=" + max + " maxIndex=" + maxIndex);
    }
}

//数组拷贝
//将 int[] arr1 = {10,20,30}; 拷贝到 arr2 数组, 要求数据空间是独立的
class ArrayCopy {
    public static void main() {
        //将 int[] arr1 = {10,20,30}; 拷贝到 arr2 数组,
        int[] arr1 = {10, 20, 30};
        //创建一个新的数组 arr2,开辟新的数据空间
        int[] arr2 = new int[arr1.length];
        //遍历 arr1 ，把每个元素拷贝到 arr2 对应的元素位置
        for (int i = 0; i < arr1.length; i++) {
            arr2[i] = arr1[i];
        }
        //修改 arr2， 不会对 arr1 有影响.
        arr2[0] = 100;
        //输出 arr1
        for (int i = 0; i < arr1.length; i++) {
            System.out.println(arr1[i]);
            //10,20,30
        }
        System.out.println("====arr2 的元素====");
        for (int i = 0; i < arr2.length; i++) {
            System.out.println(arr2[i]);
        }

    }

}

//数组反转
//要求：把数组的元素内容反转
//arr {11,22,33,44,55,66} {66, 55,44,33,22,11}

class ArrayReverse {
    public static void main() {
        //定义数组
        int[] arr = {11, 22, 33, 44, 55, 66};
        //规律
        // 1. 把 arr[0] 和 arr[5] 进行交换 {66,22,33,44,55,11}
        //2. 把 arr[1] 和 arr[4] 进行交换 {66,55,33,44,22,11}
        //3. 把 arr[2] 和 arr[3] 进行交换 {66,55,44,33,22,11}
        //4. 一共要交换 3 次 = arr.length / 2
        //5. 每次交换时，对应的下标 是 arr[i] 和 arr[arr.length - 1 -i]
        int temp = 0;
        int len = arr.length; //计算数组的长度
        for (int i = 0; i < len / 2; i++) {
            temp = arr[len - 1 - i];//保存 arr[len - 1 - i] = arr[i]; arr[i] = temp; }
            arr[len - 1 - i] = arr[i];
            arr[i] = temp;
        }
        System.out.println("===翻转后数组===");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
            //66,55,44,33,22,11
        }
        //使用逆序赋值方式
        //定义数组
        int[] arrA = {11, 22, 33, 44, 55, 66};
        //1. 先创建一个新的数组 arr2 ,大小 arrA.length
        //2. 逆序遍历 arr ,将 每个元素拷贝到 arr2 的元素中(顺序拷贝)
        //3. 建议增加一个循环变量 j -> 0 -> 5
        int[] arr2 = new int[arr.length];
        //逆序遍历 arr
        for (int i = arr.length - 1, j = 0; i >= 0; i--, j++) {
            arr2[j] = arr[i];

        }
        //4. 当 for 循环结束，arr2 就是一个逆序的数组 {66, 55, 44,33, 22, 11}
        //5. 让 arr 指向 arr2 数据空间, 此时 arr 原来的数据空间就没有变量引用
        // 会被当做垃圾，销毁
        arr = arr2;
        System.out.println("====arr 的元素情况=====");
        //6. 输出 arr 看看
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }
    }
}


/*
 * 数组添加/扩容
 * 要求：实现动态的给数组添加元素效果，实现对数组扩容。
 * 1) 原始数组使用静态分配 int[] arr = {1,2,3}
 * 2) 增加的元素 4，直接放在数组的最后 arr = {1,2,3,4}
 * */


class ArrayAdd {
    public static void main() {
        //思路分析
        //1. 定义初始数组 int[] arr = {1,2,3}//下标 0-2
        //2. 定义一个新的数组 int[] arrNew = new int[arr.length+1];
        //3. 遍历 arr 数组，依次将 arr 的元素拷贝到 arrNew 数组
        //5. 让 arr 指向 arrNew ; arr = arrNew; 那么 原来 arr 数组就被销毁
        //6. 创建一个 Scanner 可以接受用户输入
        // 7. 因为用户什么时候退出，不确定，使用 do-while + break 来控制
        Scanner myScanner = new Scanner(System.in);
        //初始化数组
        int[] arr = {1, 2, 3};
        do {
            int[] arrNew = new int[arr.length + 1];
            //遍历 arr 数组，依次将 arr 的元素拷贝到 arrNew 数组
            for (int i = 0; i < arr.length; i++) {
                arrNew[i] = arr[i];
            }
            System.out.println("请输入你要添加的元素");
            int addNum = myScanner.nextInt();
            //把 addNum 赋给 arrNew 最后一个元素
            arrNew[arrNew.length - 1] = addNum;
            //让 arr 指向 arrNew,
            arr = arrNew;
            //输出 arr 看看效果
            System.out.println("====arr 扩容后元素情况====");
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + "\t");
            }
            //问用户是否继续
            System.out.println("是否继续添加 y/n");
            char key = myScanner.next().charAt(0);
            if (key == 'n') {
                //如果输入 n ,就结束
                break;
            }
        } while (true);
        System.out.println("你退出了添加...");
    }
}

//有一个数组 {1， 2， 3， 4， 5}， 可以将该数组进行缩减，提示用户是否继续缩减，每次缩减最后那个元素。当只剩 下最后一个元素，提示，不能再缩减。
class ArrayReduce {
    public static void main() {
        Scanner myScanner = new Scanner(System.in);
        int[] arr = {1, 2, 3};
        do {
            int[] arrNew = new int[arr.length - 1];
            //遍历 arr 数组，依次将 arr 的元素拷贝到 arrNew 数组
            for (int i = 0; i < arrNew.length; i++) {
                arrNew[i] = arr[i];
            }
            //让 arr 指向 arrNew,
            arr = arrNew;
            //输出 arr 看看效果
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + "\t");
            }
            System.out.println("是否继续缩减 y/n");
            char key = myScanner.next().charAt(0);
            if (key == 'n') {
                //如果输入 n ,就结束
                break;
            }

            if (arr.length == 1) {
                System.out.println("当只剩 下最后一个元素，提示，不能再缩减。");
                break;
            }
        } while (true);
    }
}


//排序的介绍
//内部排序:
//指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择 式排序法和插入式排序法)；
//外部排序法：
//数据量过大，无法全部加载到内存中，需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。
//冒泡排序法
//冒泡排序（Bubble Sorting）的基本思想是：通过对待排序序列从后向前（从下标较大的元素开始），依次比较相邻元素 的值，若发现逆序则交换，使值较大的元素逐渐从前移向后部，就象水底下的气泡一样逐渐向上冒。


//冒泡排序法
//我们将五个无序：24,69,80,57,13 使用冒泡排序法将其排成一个从小到大的有 ,每次比较保存最大值，每轮比较　吧最大值放到最后

class BubbleSort {
    public static void main() {
        ////老韩 化繁为简，先死后活
        //数组 [24,69,80,57,13]
        //第 1 轮排序: 目标把最大数放在最后
        //第 1 次比较[24,69,80,57,13]
        //第 2 次比较[24,69,80,57,13]
        //第 3 次比较[24,69,57,80,13]
        //第 4 次比较[24,69,57,13,80]
        int[] arr = {24, 69, 80, 57, 13, -1, 30, 200, -110};
        int temp = 0; //用于辅助交换的变量
        //将多轮排序使用外层循环包括起来即可
        for (int i = 0; i < arr.length - 1; i++) {//外层循环是 4 次
            for (int j = 0; j < arr.length - 1 - i; j++) {//4 次比较-3 次-2 次-1 次
                //如果前面的数>后面的数，就交换
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
            System.out.println("\n==第" + (i + 1) + "轮==");
            for (int j = 0; j < arr.length; j++) {
                System.out.print(arr[j] + "\t");
            }
        }

    }
}

//查找
//顺序查找
//二分查找【二分法，算法讲解】

class SeqSearch {
    /*有一个数列：白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王猜数游戏：
    从键盘中任意输入一个名称，判断数列中是否包含此名称【顺序查找】
     要求: 如果找到了，就提示找到，并给出下标值
     */
    public static void main() {
        //定义一个字符串数组
        //接收用户输入, 遍历数组，逐一比较，如果有，则提示信息，并退出
        //定义一个字符串数组
        String[] names = {"白眉鹰王", "金毛狮王", "紫衫龙王", "青翼蝠王"};
        Scanner myScanner = new Scanner(System.in);
        System.out.println("请输入名字");
        String findName = myScanner.next();
        //遍历数组，逐一比较，如果有，则提示信息，并退出
        //一个编程思想/技巧, 一个经典的方法
        int index = -1;
        for (int i = 0; i < names.length; i++) {
            //比较 字符串比较 equals, 如果要找到名字就是当前元素
            if (findName.equals(names[i])) {
                System.out.println("恭喜你找到 " + findName);
                System.out.println("下标为= " + i);
                //把 i 保存到 index
                index = i;
                break;//退出 }
            }
        }
    }

    public static int recursionBinarySearch() {
        //请对一个有序数组进行二分查找 {1,8, 10, 89, 1000, 1234} ，
        // 输入一个数看看该数组是否存在此数，并且求出下标， 如果没有就提示"没有这个数"。
        //优点是比较次数少，查找速度快，平均性能好；
        //其缺点是要求待查表为有序表，且插入删除困难。
        //因此，折半查找方法适用于不经常变动而查找频繁的有序列表。
        int[] arr = {1, 8, 10, 89, 1000, 1234};
        int low = 0;
        int high = arr.length - 1;
        int key = 2;
        int middle = 0;
        if (key < arr[low] || key > arr[high] || low > high) {
            return -1;
        }
        while (low <= high) {
            middle = (low + high) / 2;
            if (arr[middle] > key) {
                //比关键字大则关键字在左区域
                high = middle - 1;
            } else if (arr[middle] < key) {
                //比关键字小则关键字在右区域
                low = middle + 1;
            } else {
                return middle;
            }
        }
        return -1;
    }

}


class TwoDimensionalArray {
    //比如我们开发一个五子棋游戏，棋盘就是需要二维数组来表示。如
    public static void main() {
        /*请用二维数组输出如下图形
        0 0 0 0 0 0
         0 0 1 0 0 0
         0 2 0 3 0 0
          0 0 0 0 0 0
          */
        //1. 从定义形式上看 int[][]
        //2. 可以这样理解，原来的一维数组的每个元素是一维数组, 就构成二维数组
        int[][] arr = {
                {0, 0, 0, 0, 0, 0},
                {0, 0, 1, 0, 0, 0},
                {0, 2, 0, 3, 0, 0},
                {0, 0, 0, 0, 0, 0}
        };
        //关于二维数组的关键概念
        System.out.println("二维数组的元素个数=" + arr.length);
        //(2) 二维数组的每个元素是一维数组, 所以如果需要得到每个一维数组的值 // 还需要再次遍历
        //(3) 如果我们要访问第 (i+1)个一维数组的第 j+1 个值 arr[i][j]; // 举例 访问 3, =》 他是第 3 个一维数组的第 4 个值 arr[2][3]
        System.out.println("第 3 个一维数组的第 4 个值=" + arr[2][3]); //3
        //输出二维图形
        for (int i = 0; i < arr.length; i++) {
            //遍历二维数组的每个元素
            //遍历二维数组的每个元素(数组)
            //老韩解读
            //1. arr[i] 表示 二维数组的第 i+1 个元素 比如 arr[0]：二维数组的第一个元素
            //2. arr[i].length 得到 对应的 每个一维数组的长度
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j] + " ");
                //输出了一维数组
            }
            System.out.println();//换行
        }
    }

}

//动态初始化
//1) 语法: 类型[][] 数组名=new 类型[大小][大小]
//2) 比如: int a[][]=new int[2][3]

class TwoDimensionalArray01 {
    public static void main() {
        //int arr[][] = new int[2][3];
        int arr[][]; //声明二维数组
        arr = new int[2][3];//再开空间
        arr[1][1] = 8;
        //遍历 arr 数组
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[i].length; j++) {//对每个一维数组遍历
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();//换行
        }
    }
}

//动态初始化
//先声明：类型 数组名[][];
//再定义(开辟空间) 数组名 = new 类型[大小][大小]
class TwoDimensionalArray03 {
    public static void main() {
        //看一个需求：动态创建下面二维数组，并输出
        //i = 0: 1
        //i = 1: 2 2
        //i = 2: 3 3 3
        //一个有三个一维数组, 每个一维数组的元素是不一样的
        //创建 二维数组，一个有 3 个一维数组，但是每个一维数组还没有开数据空间
        int[][] arr = new int[3][];
        for (int i = 0; i < arr.length; i++) {//遍历 arr 每个一维数组
            //给每个一维数组开空间 new
            //如果没有给一维数组 new ,那么 arr[i]就是 null
            arr[i] = new int[i + 1];
            //遍历一维数组，并给一维数组的每个元素赋值
            for (int j = 0; j < arr[i].length; j++) {
                arr[i][j] = i + 1;//赋值
            }
        }
        System.out.println("=====arr 元素=====");
        for (int i = 0; i < arr.length; i++) {
            //输出 arr 的每个一维数组
            for (int j = 0; j < arr[i].length; j++) {
                System.out.print(arr[i][j] + " ");
            }
            System.out.println();//换行
        }
    }
}

//静态初始化
//定义 类型 数组名[][] = {{值 1,值 2..},{值 1,值 2..},{值 1,值 2..}}

class TwoDimensionalArray04 {
    public static void main() {
        /*int arr[][]={{4,6},{1,4,5,7},{-2}}; 遍历该二维数组，并得到和 思路1. 遍历二维数组，并将各个值累计到 int sum */
        int arr[][] = {{4, 6}, {1, 4, 5, 7}, {-2}};
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            //遍历每个一维数组
            for (int j = 0; j < arr[i].length; j++) {
                sum += arr[i][j];
            }
        }
        System.out.println("sum=" + sum);
    }
}

//二维数组的应用案例
//使用二维数组打印一个 10 行杨辉三角
class YangHui {
    public static void main() {
        //1.第一行有 1 个元素, 第 n 行有 n 个元素
        //2. 每一行的第一个元素和最后一个元素都是 1
        //从第三行开始, 对于非第一个元素和最后一个元素的元素的值. arr[i][j]
        //arr[i][j] = arr[i-1][j] + arr[i-1][j-1]; //必须找到这个规律
        int[][] yangHui = new int[12][];
        for (int i = 0; i < yangHui.length; i++) {//遍历 yangHui 的每个元素
            //给每个一维数组(行) 开空间
            yangHui[i] = new int[i + 1];
            //给每个一维数组(行) 赋值
            for (int j = 0; j < yangHui[i].length; j++) {
                if (j == 0 || j == yangHui[i].length - 1) {
                    yangHui[i][j] = 1;
                } else {
                    //中间的元素
                    yangHui[i][j] = yangHui[i - 1][j] + yangHui[i - 1][j - 1];
                    System.out.println( yangHui[i][j]);
                }
            }
        }
    }
}
